package com.xy.config;

import com.alibaba.fastjson.JSON;
import com.xy.filter.LoginFilter;
import com.xy.jwt.JWToo;
import com.xy.service.MyService;
import com.xy.vo.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class MyRoleConfig extends WebSecurityConfigurerAdapter {
    /**
     * 密码加密
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 自定义用户信息
     */
    @Autowired
    public MyService myService;


    /**
     * 配置用户信息
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myService);
    }

    /**
     * 自定义登录过滤器
     */
    @Autowired
    private LoginFilter loginFilter;

    /**
     *跳转路径配置文件
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        设置自定义校验器放置UsernaemPasswordAuthenticationFilter之前
        http.addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class);
        http.formLogin()
                //登录路径
                .loginPage("/login.html")
                //登录处理路径
                .loginProcessingUrl("/login")
                //成功路径
                .successHandler(successR())
                //失败路径
                .failureHandler(failureR())
                .permitAll();
        //请求失败路径
        http.exceptionHandling().accessDeniedHandler(accessR());
        //禁用跨区伪造请求的过滤器
        http.csrf().disable();
        http.authorizeRequests()
                //其他路径需要认证
                .anyRequest().authenticated();
    }

    /**
     * 提取公共方法类Tool
     */
    public static void tool(HttpServletResponse httpServletResponse, com.xy.vo.R r) throws IOException {
        //设置编码格式
        httpServletResponse.setContentType("application/json;charset=utf-8");
        //打开流
        PrintWriter writer = httpServletResponse.getWriter();
        //json转换R类对象
        String json = JSON.toJSONString(r);
        //写入流
        writer.println(json);
        //刷新流
        writer.flush();
        //关闭流
        writer.close();
    }

    /**
     * 登录成功
     */
    private AuthenticationSuccessHandler successR(){
        return (request, response, authentication) -> {
            Map<String, Object> map = new HashMap<>();
            //获取用户名
            map.put("username", authentication.getName());
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            //权限List列表
            List<String> collect = authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
            //将权限列表放入map
            map.put("permissions", collect);
            //调用生成token静态方法
            String token = JWToo.Token(map);
            //调用流提取公共方法类
            tool(response, new com.xy.vo.R(200, "登录成功", token));
        };
    };

    /**
     * 登录失败
     */
    private AuthenticationFailureHandler failureR(){
        return (request, response, e) -> {
            //调用流提取公共方法类
            tool(response, new com.xy.vo.R(500, "登录失败",e.getMessage()));
        };
    }

    /**
     * 权限不足
     */
    private AccessDeniedHandler accessR(){
        return (request, response, e) -> {
            //调用流提取公共方法类
            tool(response, new R(403, "权限不足",e.getMessage()));
        };
    }
}
